<template>
    <li :class="cn">
        <div class="ph-node-title" >
            <ph-icon @click="toggleCollpase" v-if="node.children&&node.children.length" :icon="open?'minus-square':'plus-square'"/>
            <ph-icon @click="toggleCheck" v-if="checkable" :class="checked?'ph-checked':'ph-unchecked'" :icon="checked?'check-square':'square-o'"/>
            <span :class="checked?'ph-checked':'ph-unchecked'" @click="singleCheck">{{node.name}}</span>
        </div>
        <template v-if="node.children&&node.children.length">
            <ul class="ph-tree" v-toggle-height="open">
                <template v-for="(snode , index) in node.children" :key="index">
                    <tree-node
                    :is-root="false" 
                    :return-pid="returnPid"
                    :ref="'leaf_'+snode.id" 
                    :node="snode" 
                    :checkable="checkable" 
                    :values="values"
                    />
                </template>
            </ul>
        </template>
    </li>
</template>
<script lang="ts">

import { 
        defineComponent,
        computed,
        ref,
        toRefs,
        onBeforeMount,
        onBeforeUnmount,
        provide,
        inject,
        getCurrentInstance,
        } from 'vue'
import {PhIcon} from '../base'
import Evt from 'ph-evt'
import {ToggleHeight} from '../../directives'

const broadcast_p_check="broadcast:parent-check"
const notify_c_check="notify:child-check"
const emit_single_check = "emit:single-check"

export default defineComponent({
    name:"TreeNode",
    directives:{
        ToggleHeight
    },
    components:{
        PhIcon
    },
    props:{
        'is-root':{type:Boolean,default:false},
        'return-pid':{type:Boolean},
        node:{type:Object,default:()=>null},
        checkable:{type:Boolean,default:false},
        values:{type:Array,default:()=>[]}
    },
    setup(props){
        const ctx = getCurrentInstance()
        const myevt = new Evt();
        const pevt = inject("pevt") as Evt
        const treeRoot = inject("treeRoot") as {change:()=>void}
        const treeRootEvt = inject("treeRootEvt") as Evt
        const node = toRefs(props.node)
        const open = ref(!!node.open)
        const checked = ref(node.checked||props.values.includes(node.id.value));
        const isLeaf = computed(()=>!node.children||node.children.length==0)
        const cn = computed(()=>{
            return {
                "ph-tree-node":true,
                "ph-tree-node-open":node.open,
                "ph-tree-props-leaf":node.children&&node.children.length
            }
        })
        const bindParentCheck = (nd:{id:string|number,checked:boolean})=>{
            if(node.id!==nd.id){
                checked.value = nd.checked
                myevt.emit(broadcast_p_check,{id:node.id,checked:checked.value})
            }
        }
        const bindChildCheck = (nd:{id:string|number,checked:boolean})=>{
            if(node.id!==nd.id){
                if(nd.checked)
                    checked.value = nd.checked
                else{
                    const $refs = ctx?.refs||{}
                    const allNotChecked =Object.keys($refs).every((k:string)=>{
                        return !($refs[k] as {checked:boolean}).checked
                    })
                    if(allNotChecked)
                        checked.value = nd.checked
                }
                pevt?.emit(notify_c_check,{id:node.id,checked:checked.value})
            }
        }
        const bindSingleCheck = (id:string|number)=>{
            if(node.id.value===id)return
            checked.value = false
        }
        const getCheckededNodes = ():Array<string|number> => {
            const ids:Array<string|number> = []
            if(checked.value){
                if(isLeaf.value)ids.push(node.id.value)
                else{
                    if((props as Record<string,unknown>).returnPid)ids.push(node.id.value)
                    const $refs = ctx?.refs||{}
                    Object.keys($refs).forEach((k:string)=>{
                        const ins = $refs[k] as {checked:boolean,getCheckededNodes:()=>Array<string|number>}
                        if(ins.checked){
                            ins.getCheckededNodes().forEach((id:string|number)=>{
                                ids.push(id)
                            })
                        }
                    })
                } 
            }
            return ids
        }
        onBeforeMount(()=>{
            treeRootEvt.on(emit_single_check,bindSingleCheck)
            myevt.on(notify_c_check,bindChildCheck)
            // if((props as any).isRoot)return
            pevt.on(broadcast_p_check,bindParentCheck)
        })
        onBeforeUnmount(()=>{
            treeRootEvt.off(emit_single_check,bindSingleCheck)
            myevt.off(notify_c_check,bindChildCheck)
            // if((props as any).isRoot)return
            pevt.off(broadcast_p_check,bindParentCheck)
        })
        provide("pevt",myevt)
        return {cn,open,checked,isLeaf,myevt,pevt,getCheckededNodes,treeRoot,treeRootEvt}
    },
    methods:{
        toggleCollpase(){
            this.open = !this.open
        },
        toggleCheck(){
            this.checked = !this.checked
            this.myevt.emit(broadcast_p_check,{id:this.node.id,checked:this.checked})
            this.pevt?.emit(notify_c_check,{id:this.node.id,checked:this.checked})
            this.treeRoot.change()
        },
        singleCheck(){
            if(this.checkable)return
            if(!this.isLeaf)
                return this.toggleCollpase()
                
            this.checked = !this.checked
            this.treeRootEvt.emit(emit_single_check,this.node.id)
            this.pevt?.emit(notify_c_check,{id:this.node.id,checked:this.checked})
            this.treeRoot.change()
        }
    }
})
</script>

<style lang="scss" scoped>
.ph-tree-node{
    display: flex;
    flex-direction:column;
    flex:0 0 auto;
    padding-left:var(--ph-20);
    .ph-node-title{
        cursor: pointer;
        span{
            display: inline-block;
            padding: 0 var(--ph-5);
            &.ph-checked{
                color:var(--ph-theme)
            }
        }
        span:hover{
            background-color: var(--ph-light-l1);
        }
        i{
            margin-right: var(--ph-5);
            text-align: center;
            &.ph-checked{
                color:var(--ph-theme)
            }
        }
    }
}
</style>
